#include <dspSimulator/instruction/b.h>

bool B::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    return checkStandardCondition(dspMachine);
}

std::shared_ptr<Instruction> B::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<B> b;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b00100) && existStandardCondition(data)) {
            b = std::make_shared<B>(dspInstInfo->getAddress(), baseCycle);
            b->creg_z = extractBits<4>(data, 28);
            b->cst = extractBits<21>(data, 7).to_ulong();
            b->s = data[1];
            b->crhi = 0b00000;
            b->crlo = 0b10000;
            b->p = data[0];
            if (b->s == 0) {
                b->funcUnit = FuncUnit::S1;
            } else {
                b->funcUnit = FuncUnit::S2;
            }
            b->instFormat = InstFormat::Kind1;
        } else if (checkBits<10>(data, 2, 0b0011011000) && checkBits<5>(data, 13, 0b00000) &&
                   checkBits<5>(data, 23, 0b00000) && existStandardCondition(data)) {
            b = std::make_shared<B>(dspInstInfo->getAddress(), baseCycle);
            b->creg_z = extractBits<4>(data, 28);
            b->src2 = extractBits<5>(data, 18).to_ulong();
            b->x = data[12];
            b->s = data[1];
            b->p = data[0];
            b->funcUnit = FuncUnit::S2;
            b->instFormat = InstFormat::Kind2;
        }
    }
    return b;
}

void B::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    if (instFormat == InstFormat::Kind1) {
        if (executePhase == ExecutePhase::E6) {
            auto pce1 = *cRegPtr;
            auto pc = targetMachine->getRegisters()->getReg32ByName("pc");
            auto cst = *cstPtr;
            auto scst21 = extractBitsAndToInt<21>(cst);
            pc->updateData(pce1 + (scst21 << 2));
        }
    } else if (instFormat == InstFormat::Kind2) {
        if (executePhase == ExecutePhase::E6) {
            auto src2Reg = *src2LoRegPtr;
            targetMachine->getRegisters()->getReg32ByName("pc")->updateData(src2Reg);
        }
    }
}

std::shared_ptr<DspInstruction> B::clone() {
    return std::make_shared<B>(*this);
}

std::string B::toString() const {
    if (instFormat == InstFormat::Kind1) {
        auto pce1 = getInstructionAddress() - getInstructionAddress() % 0x20;
        auto targetAddress = pce1 + (extractBitsAndToInt<21>(cst) << 2);
        return getStandardConditionString() + " " + m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " " +
               dec2hex(targetAddress);
    } else if (instFormat == InstFormat::Kind2) {
        return getStandardConditionString() + " " + m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " " +
               getSrc2Reg32Name();
    }
    return {};
}